package com.hexgon.plugins.rabbitmq;

import com.hexgon.common.config.AsyncConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory;
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitAdmin;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.rabbit.listener.RabbitListenerEndpointRegistry;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.amqp.support.converter.MessageConverter;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.amqp.RabbitProperties;
import org.springframework.boot.autoconfigure.amqp.RabbitRetryTemplateCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import org.springframework.util.StringUtils;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * MultiRabbitAutoConfiguration 装配多个RabbitMQ
 * @author: XuZhibin
 * @date: 2023/11/12 11:08
 * @version: v1.0
 * @attention:
 */
@EnableMultiRabbit
@Configuration
public class MultiRabbitAutoConfiguration {

    private final static Logger LOGGER = LoggerFactory.getLogger(MultiRabbitAutoConfiguration.class);

    private final static String CONNECTION_FACTORY = "%sConnectionFactory";
    private final static String RABBIT_LISTENER_CONTAINER_FACTORY = "%sRabbitListenerContainerFactory";
    private final static String RABBIT_ADMIN = "%sRabbitAdmin";
    private final static String RABBIT_TEMPLATE = "%sRabbitTemplate";

    @Resource
    private DefaultListableBeanFactory defaultListableBeanFactory;
    @Resource
    private MultiRabbitProperties multiRabbitProperties;
    @Resource
    private MultiRabbitListenerContainerFactoryConfigurer multiRabbitListenerContainerFactoryConfigurer;


    @PostConstruct
    public void initRabbitConfig() {
        Map<String, RabbitProperties> multi = multiRabbitProperties.getCollections();
        Set<Map.Entry<String, RabbitProperties>> entries = multi.entrySet();
        for (Map.Entry<String, RabbitProperties> rabbitProps : entries) {
            String rabbitName = rabbitProps.getKey();
            RabbitProperties rabbitProp = rabbitProps.getValue();

            boolean excludeRabbit = excludeRabbit(rabbitName);

            // 注册 CachingConnectionFactory
            CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
            connectionFactory.setHost(rabbitProp.getHost());
            connectionFactory.setPort(rabbitProp.getPort());
            connectionFactory.setUsername(rabbitProp.getUsername());
            connectionFactory.setPassword(rabbitProp.getPassword());
            connectionFactory.setVirtualHost(rabbitProp.getVirtualHost());
            connectionFactory.setPublisherConfirmType(rabbitProp.getPublisherConfirmType());
            connectionFactory.setPublisherReturns(rabbitProp.isPublisherReturns());
            String factoryName = String.format(CONNECTION_FACTORY, rabbitName);
            defaultListableBeanFactory.registerSingleton(factoryName, connectionFactory);

            // 注册 SimpleRabbitListenerContainerFactory
            SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
            factory.setMessageConverter(new Jackson2JsonMessageConverter());
            if (excludeRabbit) {
                factory.setAutoStartup(false);
            } else {
                factory.setAutoStartup(true);
            }
            multiRabbitListenerContainerFactoryConfigurer.configure(rabbitProp, factory, connectionFactory);
            String containerFactoryName = String.format(RABBIT_LISTENER_CONTAINER_FACTORY, rabbitName);
            defaultListableBeanFactory.registerSingleton(containerFactoryName, factory);

            // 注册RabbitAdmin
//            BeanDefinition adminBeanDefinition = BeanDefinitionBuilder.genericBeanDefinition(RabbitAdmin.class)
//                    .addConstructorArgValue(connectionFactory)
//                    .addPropertyValue("autoStartup", true)
//                    .getBeanDefinition();
            BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(RabbitAdmin.class)
                    .addConstructorArgValue(connectionFactory);
            if (excludeRabbit) {
                beanDefinitionBuilder.addPropertyValue("autoStartup", false);
            } else {
                beanDefinitionBuilder.addPropertyValue("autoStartup", true);
            }
            BeanDefinition adminBeanDefinition = beanDefinitionBuilder.getBeanDefinition();

            String adminName = String.format(RABBIT_ADMIN, rabbitName);
            defaultListableBeanFactory.registerBeanDefinition(adminName, adminBeanDefinition);

            // 注册RabbitTemplate
            String templateName = String.format(RABBIT_TEMPLATE, rabbitName);
            RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
            rabbitTemplate.setMessageConverter(new Jackson2JsonMessageConverter());
            rabbitTemplate.setMandatory(true);
            rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
                @Override
                public void confirm(CorrelationData correlationData, boolean ack, String cause) {
                    // 当没有找到Exchange时 ack=false
                    if (ack) {
                        LOGGER.info("消息成功发送到交换器");
                    } else {
                        LOGGER.error("消息发送失败，错误信息：" + cause);
                    }
                }
            });
            rabbitTemplate.setReturnCallback(new RabbitTemplate.ReturnCallback() {
                @Override
                public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
                    // 成功找到Exchange，但是无法路由到指定队列时触发
                    LOGGER.error("消息发送失败，错误信息：" + replyCode + " -> " + replyText);
                }
            });
            defaultListableBeanFactory.registerSingleton(templateName, rabbitTemplate);

        }
    }

    /**
     * 是否再排除的RabbitMQ
     * @param rabbitName
     * @return
     */
    private boolean excludeRabbit(String rabbitName) {
        // 需要排除的RabbitMQ
        String excludeRabbits = multiRabbitProperties.getExcludeRabbits();
        if (!StringUtils.isEmpty(excludeRabbits)) {
            String[] excludeRabbitArr = excludeRabbits.split(",");
            for (String excludeRabbit : excludeRabbitArr) {
                if (excludeRabbit.equalsIgnoreCase(rabbitName)) {
                    return true;
                }
            }
        }
        return false;
    }


    @Bean
    public MultiRabbitListenerContainerFactoryConfigurer multiSimpleRabbitListenerContainerFactoryConfigurer(
            ObjectProvider<MessageConverter> messageConverter,
            ObjectProvider<RabbitRetryTemplateCustomizer> retryTemplateCustomizers) {
        MultiRabbitListenerContainerFactoryConfigurer configurer = new MultiRabbitListenerContainerFactoryConfigurer();
        configurer.setMessageConverter((MessageConverter)messageConverter.getIfUnique());
        configurer.setRetryTemplateCustomizers((List)retryTemplateCustomizers.orderedStream().collect(Collectors.toList()));
        return configurer;
    }

}
